import { v2, v3, Vec2, Vec3 } from 'cc';

/* eslint-disable */
export namespace MathUtil {
  enum NumberUnit {
    /** 万 */
    W = 1e4,
    /** 十万 */
    S = 1e5,
    /** 百万 */
    M = 1e6,
    /** 千万 */
    MM = 1e7,
    /** 亿 */
    E = 1e8,
    /** 兆 */
    T = 1e12,
    /** 京 */
    P = 1e16,
  }
  /**
   * 计算椭圆的弧长
   * @param a 椭圆的长轴半径
   * @param b 椭圆的短轴半径
   * @param startAngle 起始角度（弧度）
   * @param endAngle 结束角度（弧度）
   * @param steps 数值积分的分段数（默认为 1000）
   * @returns 椭圆的弧长
   */
  export function ellipseArcLength(a: number, b: number, startAngle: number, endAngle: number, steps: number = 1000): number {
    // 确保角度范围合理
    if (startAngle > endAngle) {
      [startAngle, endAngle] = [endAngle, startAngle];
    }

    // 数值积分（辛普森法则）
    let arcLength = 0;
    const deltaTheta = (endAngle - startAngle) / steps;

    for (let i = 0; i < steps; i++) {
      const theta1 = startAngle + i * deltaTheta;
      const theta2 = startAngle + (i + 1) * deltaTheta;

      // 计算积分函数的值
      const f1 = Math.sqrt(a ** 2 * Math.sin(theta1) ** 2 + b ** 2 * Math.cos(theta1) ** 2);
      const f2 = Math.sqrt(a ** 2 * Math.sin(theta2) ** 2 + b ** 2 * Math.cos(theta2) ** 2);

      // 辛普森法则
      arcLength += (deltaTheta / 6) * (f1 + 4 * Math.sqrt(a ** 2 * Math.sin((theta1 + theta2) / 2) ** 2 + b ** 2 * Math.cos((theta1 + theta2) / 2) ** 2) + f2);
    }

    return arcLength;
  }

  export function numberFormat(value: number, bitmap: number = 1) {
    let str = '';
    if (value >= NumberUnit.T) {
      let temp = value / NumberUnit.T;
      let count = 12;
      while (temp / 10 >= 1) {
        temp /= 10;
        count++;
      }
      const retStr = temp.toString();
      if (retStr.substring(retStr.indexOf('.') + 1).length >= bitmap) {
        str += retStr.substring(0, retStr.indexOf('.') + 1 + bitmap);
      } else {
        str += retStr;
      }
      str += `e+${count}`;
    } else {
      str = this.getLocaleString(value);
    }
    return str;
  }

  export function getLocaleString(num: number) {
    const str = num.toString();
    const dotIndex = str.indexOf('.');
    const frontStr = dotIndex != -1 ? str.substring(0, dotIndex) : str.substring(0);
    const backStr = dotIndex != -1 ? str.substring(dotIndex) : '';
    const strArr = [];
    const retArr = [];
    let beginIndex = frontStr.length % 3;
    const initIndex = beginIndex;
    if (frontStr.length <= 3) {
      retArr.push(frontStr);
    } else
      while (beginIndex < frontStr.length) {
        const s = frontStr.slice(beginIndex, beginIndex + 3);
        strArr.push(s);
        beginIndex += 3;
        if (beginIndex >= frontStr.length) {
          initIndex > 0 && retArr.push(frontStr.slice(0, initIndex));
          retArr.push(...strArr);
        }
      }

    let retStr = '';
    while (retArr.length) {
      retStr += retArr.shift();
      retArr.length > 0 && (retStr += ',');
    }

    retStr += backStr;
    return retStr;
  }

  /** 转科学计数 */
  export function number2string(num: number) {
    const strArr = ['k', 'm', 'g', 't'];
    if (num >= Math.pow(1000, 4)) {
      return (num / Math.pow(1000, 4)).toFixed(1) + strArr[3];
    } else if (num >= Math.pow(1000, 3)) {
      return (num / Math.pow(1000, 3)).toFixed(1) + strArr[2];
    } else if (num >= 10000000) {
      return (num / Math.pow(1000, 2)).toFixed(1) + strArr[1];
      // } else if (num >= Math.pow(1000, 2)) {
      //   return (num / Math.pow(1000, 2)).toFixed(1) + strArr[1];
      // } else if (num >= Math.pow(10000, 1)) {
      //   return (num / Math.pow(1000, 1)).toFixed(1) + strArr[0];
    } else {
      return num;
    }
  }

  export function number2string2(num: number) {
    const strArr = ['k', 'm', 'g', 't'];
    if (num >= Math.pow(1000, 4)) {
      return (num / Math.pow(1000, 4)).toFixed(1) + strArr[3];
    } else if (num >= Math.pow(1000, 3)) {
      return (num / Math.pow(1000, 3)).toFixed(1) + strArr[2];
    } else if (num >= Math.pow(1000, 2)) {
      return (num / Math.pow(1000, 2)).toFixed(1) + strArr[1];
    } else if (num >= 10000) {
      return (num / 1000).toFixed(1) + strArr[0];
      // } else if (num >= Math.pow(1000, 2)) {
      //   return (num / Math.pow(1000, 2)).toFixed(1) + strArr[1];
      // } else if (num >= Math.pow(10000, 1)) {
      //   return (num / Math.pow(1000, 1)).toFixed(1) + strArr[0];
    } else {
      return num;
    }
  }
  /** 生成一个在[min, max)之间的随机数 */
  export function randomBetween(min: number, max: number): number {
    if (max <= min) return max;
    return min + Math.random() * (max - min);
  }
  /** 生成一个在[min, max)之间的随机数 */
  export function randomBetweenInclude(min: number, max: number): number {
    if (max <= min) return max;
    return min + Math.random() * (max - min - 0.01);
  }

  /** 随机生成一个整数，范围为[min, max) */
  export function randomIntegerBetween(min: number, max: number): number {
    return Math.floor(randomBetween(min, max - 0.01));
  }

  /** 随机生成一个整数，范围为[min, max] */
  export function randomIntegerInclude(min: number, max: number): number {
    return Math.floor(randomBetween(min, max + 0.99));
  }

  /** 随机生成一个整数，范围为[min, max) */
  export function randomIntegerBetweenByString(range: number[]): number {
    return Math.floor(randomBetween(range[0], range[1]));
  }

  /** 随机生成x个不重复的整数，范围为[n, m) */
  export function randomUniqueIntegers(n: number, m: number, x: number): number[] {
    if (x > m - n) {
      return Array.from({ length: m - n }, (_, i) => n + i);
    }

    const range = Array.from({ length: m - n }, (_, i) => n + i);
    const result: number[] = [];
    for (let i = 0; i < x; i++) {
      const randomIndex = Math.floor(Math.random() * (range.length - i)) + i;
      [range[i], range[randomIndex]] = [range[randomIndex], range[i]];
      result.push(range[i]);
    }

    return result;
  }

  /** 随机生成x个不重复的整数，范围为[n, m] */
  export function randomUniqueIntegersInclude(n: number, m: number, x: number): number[] {
    if (x > m - n + 1) {
      return Array.from({ length: m - n + 1 }, (_, i) => n + i);
    }

    const range = Array.from({ length: m - n + 1 }, (_, i) => n + i);
    const result: number[] = [];

    for (let i = 0; i < x; i++) {
      const randomIndex = Math.floor(Math.random() * (range.length - i)) + i;
      [range[i], range[randomIndex]] = [range[randomIndex], range[i]];
      result.push(range[i]);
    }

    return result;
  }

  /** 随机生成x个不重复的数，范围为[n, m) */
  export function randomUnique(n: number, m: number, x: number): number[] {
    const result: number[] = [];
    let i = 0;
    while (i < x) {
      const random = randomBetween(n, m);
      if (!result.includes(random)) {
        result.push(random);
        i++;
      }
    }
    return result;
  }

  /** 随机生成x个不重复的数，范围为[n, m] */
  export function randomUniqueInclude(n: number, m: number, x: number): number[] {
    const result: number[] = [];
    let i = 0;
    while (i < x) {
      const random = randomBetweenInclude(n, m);
      if (!result.includes(random)) {
        result.push(random);
        i++;
      }
    }
    return result;
  }

  /** 随机生成一个不重复的整数，范围为[n, m]，x为排除的整数 */
  export function randomIntegerExceptX(n: number, m: number, x: number): number {
    if (n > m || (n === m && n === x)) {
      return n;
    }

    let result: number;
    do {
      result = Math.floor(Math.random() * (m - n + 1)) + n;
    } while (result === x);

    return result;
  }

  /** 随机生成x个整数，范围为[n, m) */
  export function randomNumbers(n: number, m: number, x: number): number[] {
    if (n == m) {
      return Array.from({ length: x }, () => n);
    }
    if (n > m) {
      [n, m] = [m, n];
    }
    const range = m - n;
    const arr = Array.from({ length: x }, () => Math.floor(Math.random() * range + n));
    return arr;
  }

  /** 随机生成x个整数，范围为[n, m] */
  export function randomNumbersInclude(n: number, m: number, x: number): number[] {
    if (n == m) {
      return Array.from({ length: x }, () => n);
    }
    if (n > m) {
      [n, m] = [m, n];
    }
    const range = m - n + 1;
    const arr = Array.from({ length: x }, () => Math.floor(Math.random() * range + n));
    return arr;
  }

  /** 快速开方 */
  export function fastSqrt(number: number, tolerance: number = 1e-7): number {
    if (number < 0) {
      throw new Error('MathUtil:fastSqrt() error： 不能对负数开方');
    }
    if (number === 0 || number === 1) {
      return number;
    }

    let guess = number / 2;
    while (true) {
      const nextGuess = (guess + number / guess) / 2;
      if (Math.abs(nextGuess - guess) < tolerance) {
        return nextGuess;
      }
      guess = nextGuess;
    }
  }

  export function UUID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      const r = (Math.random() * 16) | 0,
        v = c === 'x' ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
  }

  export function shortUUID(): string {
    return 'xx4xxyxx'.replace(/[xy]/g, function (c) {
      const r = (Math.random() * 16) | 0,
        v = c === 'x' ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
  }

  export function formatNumber(num: number, count: number): number {
    return parseFloat(num.toFixed(count));
  }

  /** 最大公约数 */
  export function gcd(a: number, b: number): number {
    if (b == 0) {
      return a;
    } else {
      return gcd(b, a % b);
    }
  }

  /** 最小公倍数 */
  export function lcm(a: number, b: number): number {
    return Math.abs(a * b) / gcd(a, b);
  }

  /** 保留小数 */
  export function toFixed(num: number, count: number = 0): number {
    const t = Math.pow(10, count);
    const temp = Math.floor(num * t) / t;
    return temp;
  }

  /** 保留小数（四色五入） */
  export function toFixedPro(num: number, count: number = 0): number {
    const t = Math.pow(10, count);
    const temp = Math.round(num * t) / t;
    return temp;
  }

  /** 计算两点间的距离 */
  export function distance(pos1: Vec2 | Vec3, pos2: Vec2 | Vec3): number {
    if (pos1 instanceof Vec2 && pos2 instanceof Vec2) {
      return fastSqrt(Math.abs((pos1.x - pos2.x) ** 2 + (pos1.y - pos2.y) ** 2));
    } else if (pos1 instanceof Vec3 && pos2 instanceof Vec3) {
      return fastSqrt(Math.abs((pos1.x - pos2.x) ** 2 + (pos1.y - pos2.y) ** 2 + (pos1.z - pos2.z) ** 2));
    }
  }

  /** 计算与原点的距离 */
  export function distanceToOrigin<T extends Vec2>(x: T): number;
  export function distanceToOrigin<T extends Vec3>(x: T): number;
  export function distanceToOrigin<T extends number>(x: T, y: number): number;
  export function distanceToOrigin<T extends Vec2 | Vec3 | number>(x: T, y?: number): number {
    if (typeof x === 'number') {
      return fastSqrt(x ** 2 + y ** 2);
    } else {
      return fastSqrt(Math.abs(x.x ** 2 + x.y ** 2));
    }
  }

  /**
   * 根据位矩形置随机获得的一个点
   * @param pos 坐标
   * @param ww 宽的一半
   * @param hh 高的一半
   * @param out
   */
  export function randomPointInRect2D<T extends Vec3>(pos: T, ww: number, hh: number, out?: Vec2 | Vec3): T;
  export function randomPointInRect2D<T extends Vec2>(pos: T, ww: number, hh: number, out?: Vec2 | Vec3): T {
    if (!out) out = pos instanceof Vec2 ? v2() : v3();
    out.x = pos.x + randomIntegerInclude(-ww, ww);
    out.y = pos.y + randomIntegerInclude(-hh, hh);
    return out as T;
  }

  /**
   * 限制在[min. max]区间内
   */
  export function clamp(value: number, min: number, max: number): number {
    const range = max - min;
    let newValue = value - min;
    if (newValue < 0) {
      newValue += range;
    }
    return newValue + min;
  }

  /**
   * 计算原点与点的射线和y轴向上方向的角度
   * @param sourcePos 原点坐标
   * @param pos 坐标点
   * @param radius 对比角度(0-360表示y轴（↑）旋转的角度)
   * @param direction 方向 -1表示顺时针 1表示逆时针
   * @return 角度 (0-360)
   */
  export function getAngle<T extends Vec3>(sourcePos: T, pos: T, direction: 1 | -1 = 1, radius: number = 0): number {
    const [x1, y1] = [pos.x - sourcePos.x, pos.y - sourcePos.y];
    const [x2, y2] = [0, 1];
    if ((!x1 && !y1) || (!x2 && !y2)) return 0;
    const cos = (x1 * x2 + y1 * y2) / (distanceToOrigin(x1, y1) * distanceToOrigin(x2, y2));
    const angle = (Math.acos(cos) * 180) / Math.PI;
    const symbol = x1 * y2 - x2 * y1 < 0 ? -1 : 1;
    if (symbol == direction) {
      return angle + radius;
    }
    return 360 - angle + radius;
  }

  /** 获取抛物线的最高点
   *  @param pos1 抛物线起点
   *  @param pos2 抛物线终点
   *  @param height 抛物线高度
   *  @param direction 抛物线方向:1/-1 (最高点的x坐标减两点连线的中点的x坐标，为正表示向右，为负表示向左)
   *  @param out 输出
   */
  export function getParabolaHighestPoint(pos1: Vec2, pos2: Vec2, height: number, out?: Vec2, direction?: number): Vec2;
  export function getParabolaHighestPoint(pos1: Vec3, pos2: Vec3, height: number, out?: Vec3, direction?: number): Vec3;
  export function getParabolaHighestPoint(pos1: Vec2 | Vec3, pos2: Vec2 | Vec3, height: number, out?: Vec2 | Vec3, direction: number = 1): Vec2 | Vec3 {
    if (!out) out = pos1 instanceof Vec2 ? v2() : v3();
    const [x1, y1] = [pos1.x, pos1.y];
    const [x2, y2] = [pos2.x, pos2.y];
    const [x3, y3] = [x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2];
    //计算两点连线的斜率
    // const a1 = (y1 - y2) / x1 - x2;
    // const b1 = y1 - a1 * x1;
    //计算垂直的直线
    const a2 = -(y1 - y2) / x1 - x2;
    const b2 = y3 - a2 * x3;
    //与x轴的连线长度
    const tempx = -b2 / a2;
    const xx = x3 - tempx;
    const long = fastSqrt(xx ** 2 + y3 ** 2);
    const x = (xx / height) * long;
    const longx = tempx + xx - x;
    const longy = fastSqrt(long ** 2 - out.x ** 2);
    if (direction) {
      out.x = x3 + longx;
      out.y = y3 + longy;
    } else {
      out.x = x3 - longx;
      out.y = y3 - longy;
    }
    return out;
  }
}
